package model;

import java.util.ArrayList;

public class Board {
	private Piece[] pieces;

	public Board() {
		pieces = new Piece[64];
		for (int i = 0; i < 64; i++) {
			pieces[i] = Piece.EMPTY;
		}
	}

	/*
	 * Create a new board by copy
	 */
	public Board(Board b) {
		pieces = new Piece[64];
		for (int i = 0; i < 64; i++) {
			pieces[i] = b.pieces[i];
		}
		turn = b.turn;
	}

	/**
	 * Initialise the board for a new game
	 */
	public void initBoard() {

		// set up white pieces
		setPiece(Piece.WR, Square.A1);
		setPiece(Piece.WR, Square.H1);

		setPiece(Piece.WN, Square.B1);
		setPiece(Piece.WN, Square.G1);

		setPiece(Piece.WB, Square.C1);
		setPiece(Piece.WB, Square.F1);

		setPiece(Piece.WQ, Square.D1);
		setPiece(Piece.WK, Square.E1);

		setPiece(Piece.WP, Square.A2);
		setPiece(Piece.WP, Square.B2);
		setPiece(Piece.WP, Square.C2);
		setPiece(Piece.WP, Square.D2);
		setPiece(Piece.WP, Square.E2);
		setPiece(Piece.WP, Square.F2);
		setPiece(Piece.WP, Square.G2);
		setPiece(Piece.WP, Square.H2);

		// set up black pieces
		setPiece(Piece.BR, Square.A8);
		setPiece(Piece.BR, Square.H8);

		setPiece(Piece.BN, Square.B8);
		setPiece(Piece.BN, Square.G8);

		setPiece(Piece.BB, Square.C8);
		setPiece(Piece.BB, Square.F8);

		setPiece(Piece.BQ, Square.D8);
		setPiece(Piece.BK, Square.E8);

		setPiece(Piece.BP, Square.A7);
		setPiece(Piece.BP, Square.B7);
		setPiece(Piece.BP, Square.C7);
		setPiece(Piece.BP, Square.D7);
		setPiece(Piece.BP, Square.E7);
		setPiece(Piece.BP, Square.F7);
		setPiece(Piece.BP, Square.G7);
		setPiece(Piece.BP, Square.H7);

	}

	/**
	 * Set a piece to the given square
	 * 
	 * @param piece
	 * @param square
	 */
	public void setPiece(Piece piece, Square square) {
		pieces[square.ordinal()] = piece;
	}

	/**
	 * Get the content of a given square
	 * 
	 * @param square
	 * @return
	 */
	public Piece getPiece(Square square) {
		int index = square.ordinal();
		if(index>-1 && index <64){
			return pieces[index];
		}
		return Piece.OUT_OF_RANGE;
	}

	public Piece getPiece(int index) {
		return pieces[index];
	}

	// TODO put those constant values in a separate class

	private int turn;
	private int WHITE_TO_MOVE = 0;
	private int BLACK_TO_MOVE = 1;

	public void setWhiteToMove() {
		turn = WHITE_TO_MOVE;
	}

	public void setBlackToMove() {
		turn = BLACK_TO_MOVE;
	}

	public boolean isWhiteToMove() {
		return (turn == WHITE_TO_MOVE);
	}

	public boolean isBlackToMove() {
		return (turn == BLACK_TO_MOVE);
	}

	/*
	 * Change the turn
	 */
	private int changeTurn() {
		// turn = 1 - turn;
		turn = (turn == WHITE_TO_MOVE) ? (BLACK_TO_MOVE) : (WHITE_TO_MOVE);
		return turn;
	}

	public int getTurn() {
		return turn;
	}

	/**
	 * Indicates if the piece is playable for the given player (only check the
	 * color)
	 * 
	 * @param square
	 * @return
	 */
	public boolean isPiecePlayable(Square square) {
		Piece pieceCode = getPiece(square);
		if (turn == WHITE_TO_MOVE) {
			return isPieceWhite(pieceCode);
		} else {
			return isPieceBlack(pieceCode);
		}
	}

	public boolean isPieceWhite(Piece piece) {
		return (piece == Piece.WP || piece == Piece.WR || piece == Piece.WN
				|| piece == Piece.WB || piece == Piece.WQ || piece == Piece.WK);
	}

	public boolean isPieceBlack(Piece piece) {
		return (piece == Piece.BP || piece == Piece.BR || piece == Piece.BN
				|| piece == Piece.BB || piece == Piece.BQ || piece == Piece.BK);
	}

	
	/**
	 * Get EVERY possible moves for the position (some of them might be unlegal move)
	 * @return
	 */
	/*
	@Deprecated
	public ArrayList<Move> getPossibleMoves() {
		// TODO : implement this method correctly (partially duplicate the code from class Engine to this
		// one)
		Engine moveChecker = new Engine();
		ArrayList<Move> possibleMoves = moveChecker.getPossibleMoves(this);
		//DebugDisplayPossibleMoves(possibleMoves);
		return possibleMoves;
	}*/
	
	/**
	 * Get all the legal moves for the given position
	 * @return
	 */
	public ArrayList<Move> getLegalMoves() {
		ArrayList<Move> legalMoves = Engine.getLegalMoves(this);
		//DebugDisplayMoves(possibleMoves);
		return legalMoves;
	}

	/*
	private void DebugDisplayPossibleMoves(ArrayList<Move> possibleMoves) {
		System.out.println("Possible moves:");
		for(Move m : possibleMoves){
			System.out.print(m.toString() + " ");
		}
		System.out.println();
	}
	*/

	/**
	 * Return if the square is empty or occupied by a piece of the opponent
	 * 
	 * @param square
	 * @return
	 */
	public boolean isEmptyOrOpponent(Square square) {
		Piece piece = getPiece(square);
		if (piece == Piece.EMPTY)
			return true;
		if (isWhiteToMove())
			return isPieceBlack(piece);
		if (isBlackToMove())
			return isPieceWhite(piece);
		return false;
	}

	/**
	 * Perform a basic move or basic capture (no change of the turn)
	 * 
	 * @param start
	 * @param dest
	 */
	private void move(Square start, Square dest) {
		pieces[dest.ordinal()] = pieces[start.ordinal()];
		pieces[start.ordinal()] = Piece.EMPTY;
	}

	/**
	 * Perform the move and change turn
	 * 
	 * @param m
	 */
	public void Play(Move m) {
		Piece piece = this.getPiece(m.begin);
		Piece dest =  this.getPiece(m.end);
		// En-passant extra-move (remove opponant's pawn)
		if(dest == Piece.EMPTY && m.begin.getCol()!=m.end.getCol()){
			if(piece == Piece.WP){
				setPiece(Piece.EMPTY, Square.get(m.begin.getCol(), m.begin.getRow()+1));
			}	
			if(piece == Piece.BP){
				setPiece(Piece.EMPTY, Square.get(m.begin.getCol(), m.begin.getRow()-1));
			}
		}
		
		// Castleling extra-move (Rook)
		if( piece == Piece.WK && m.begin == Square.E1 && m.end == Square.G1){
			move(Square.H1,Square.F1);
		}
		if( piece == Piece.WK && m.begin == Square.E1 && m.end == Square.C1){
			move(Square.A1,Square.D1);
		}
		if( piece == Piece.BK && m.begin == Square.E8 && m.end == Square.G8){
			move(Square.H8,Square.F8);
		}
		if( piece == Piece.BK && m.begin == Square.E8 && m.end == Square.C8){
			move(Square.A8,Square.D8);
		}

		
		// standard move
		move(m.begin, m.end);
		changeTurn();
	}

	/**
	 * Indicates if the square is empty
	 * 
	 * @param square
	 * @return
	 */
	public boolean isSquareEmpty(Square square) {
		return (pieces[square.ordinal()] == Piece.EMPTY);
	}

}
